package com.naruto.chapter01;

public class _001HashMapChanges {

//1.8之前的HashMap
//设计为数组+链表结构(散列桶)
//初始大小16( 1 << 4),扩容因子0.75,最大1 << 30
//扩容也很消耗性能,所以初始化时需要根据数据量来设定容量

// 1.当调用map.put(key, value)时,先对key求hashCode值,再对hash根据map容量取%(模)
// 查找map中是否含有取余后的值
// 有则对比原先的key值是否相等,相等就替换,不等产生链表,追加在该位置的链表头上(后加的放前面)
// (这就是重写hashCode必须要重写equals,确保同样的数据在hash冲突时,equals是相同的,不同会产生错误的数据追加)
// 例:之前对整个string求hash,现在业务变更只要前三位,就会出现本应该被覆盖的,却被追加到链表头上
// 没有则直接放入数组

// 没有hash冲突时,单纯的数组,查询最快
// 有hash冲突时,查询到冲突位置,需要通过equals来比对key,当冲突过多效率下降很快


//1.8之后的HashMap
//设计为数组-链表(追加到末尾)-红黑树(用于特定情况下替换链表)
//当单条链表数据>8条时,map总容量大于64的时候,链表会被红黑树替换(红黑树,添加比链表慢;查询,删除比链表快)
//扩容时红黑树处理,树上元素无需全部重排(后续研究!!!)

//注意: HashMap变了,那么HashSet也就变了
//HashSet 内部使用 HashMap 。HashSet 将存储的值作为 key 存入HashMap）

    // ConcurrentHashMap
    //1.8之前,ConcurrentHashMap默认的并发级别是16 (concurrentLevel)(16个segment块,相当于16条关系数据库的行,然后使用行级锁)
    //1.8之后,segment就只是个形式,为了向前兼容,而使用的并发控制是CAS(无锁机制)

//一个CAS方法包含三个参数CAS(V,E,N)。V表示要更新的变量，E表示预期的值，N表示新值。只有当V的值等于E时，才会将V的值修改为N。如果V的值不等于E，说明已经被其他线程修改了，当前线程可以放弃此操作，也可以再次尝试次操作直至修改成功。基于这样的算法，CAS操作即使没有锁，也可以发现其他线程对当前线程的干扰（临界区值的修改），并进行恰当的处理。
    //    额外引申技术点：volatile
    //上面说到当前线程可以发现其他线程对临界区数据的修改，这点可以使用volatile进行保证。
    //volatile实现了JMM中的可见性。使得对临界区资源的修改可以马上被其他线程看到，它是通过添加内存屏障实现的。

//未锁定 	01 	对象哈希码_对象分代年龄
//轻量级锁定 	00 	指向锁记录的指针
//膨胀(重量级锁定) 	10 	执行重量级锁定的指针
//GC标记 	11 	空(不需要记录信息)
//可偏向 	01 	偏向线程ID_偏向时间戳_对象分代年龄


    //1.8之前,内存模型: 栈,堆,方法区(属于堆内存的永久区的一部分,主要加载类信息,垃圾回收条件极其苛刻(方法区快满时))[Hotspot 还是将永久区包含在堆内]
    //1.8之后,hotspot也将永久区(PermGen)剥离出来,叫做元空间(Metaspace),使用物理内存,而不使用JVM分配的内存(可使用的空间更大了,OOM概率更低)
    //PremGenSize - MaxPremGenSize  被 MetaSpaceSize - MaxMetaSpaceSize内存控制参数取代


}
